#pragma once
#include <iostream>
#include <string>
#include <fstream>
#include <mutex>
#include <cstring>
#include <unordered_map>
#include <unistd.h>
#include <vector>
#include <algorithm>
#include <fstream>
#include <boost/filesystem.hpp>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "cppjieba/Jieba.hpp"
#include "Log.hpp"

namespace Util
{
    using namespace ns_log;
    static const std::string SEP = "\3";
    static const std::string LINE_BREAKS = "\n";
    static const std::string SRC_PATH = "/home/ubuntu/Lhd/Project/Boost_Search_Engines/data/input";
    static const std::string RAW_PATH = "/home/ubuntu/Lhd/Project/Boost_Search_Engines/data/output/raw.txt";
    static const std::string HTML_SUGFFIX = ".html";
    struct DocInfo
    {
        //.html文件中的标题
        std::string _title;
        std::string _content;
        //.html在boost官网对应的url地址
        std::string _url;
    };
    using DocInfo_t = DocInfo;
    class FileTool
    {
    public:
        static size_t file_size(const std::string &filename)
        {
            struct stat filest;
            size_t _result = stat(filename.c_str(), &filest);
            if (_result < 0)
            {
                LOG(WARNING, "获取文件%s大小失败,异常信息:%s\n", filename.c_str(), strerror(errno));
                return -1;
            }
            return filest.st_size;
        }
        // 读取文件
        static bool ReadFileToString(const std::string &filename, std::string *out)
        {
            std::ifstream in(filename, std::ios::in | std::ios::binary);
            if (!in.is_open())
            {
                LOG(ERROR, "打开文件%s时异常!\n");
                return false;
            }

            out->resize(file_size(filename));

            in.read(&((*out)[0]), out->size());

            if (!in.good())
            {
                LOG(ERROR, "读取文件%s时异常!\n");
                return false;
            }
            return true;
        }
        static bool WriteStringToFile(const std::string &filename, const std::string &data)
        {
            std::ofstream out(filename, std::ios::in | std::ios::binary | std::ios::app);
            if (!out.is_open())
            {
                LOG(ERROR, "打开文件%s时异常!\n");
                return false;
            }
            out.write(data.c_str(), data.size());
            if (!out.good())
            {
                LOG(ERROR, "写入文件%s时异常!\n");
                return false;
            }
            return true;
        }
    };
    class ParserUtil
    {
    private:
        static bool ParseTitle(const std::string &data, std::string *title)
        {
            // 0123456      13      20
            //<title>xxxxxx</title>
            auto pos = data.find("<title>");
            if (pos == std::string::npos)
            {
                return false;
            }
            size_t start = pos + 7;
            size_t end = data.find("</title>");
            if (end == std::string::npos)
            {
                return false;
            }
            if (start > end)
            {
                return false;
            }
            *title = data.substr(start, end - start);
            return true;
        }
        static bool ParseContent(std::string &data, std::string *content)
        {
            // 去标签
            // 简易状态机
            typedef enum STATU
            {
                LABEL,
                CONTENT
            } STATU;
            static STATU statu = LABEL;
            for (auto &ch : data)
            {
                switch (statu)
                {
                case LABEL:
                    if (ch == '>')
                    {
                        statu = CONTENT;
                    }
                    break;
                case CONTENT:
                    if (ch == '<')
                    {
                        statu = LABEL;
                    }
                    else
                    {
                        if (ch == '\n')
                            ch = ' ';
                        else
                            (*content) += ch;
                    }
                    break;
                }
            }
            return true;
        }
        // 解析url
        static bool ParseUrl(const std::string &file, std::string *url)
        {
            static std::string utl_head = "https://www.boost.org/doc/libs/1_87_0/doc/html";
            std::string url_tail = file.substr(SRC_PATH.size());
            *url = utl_head + url_tail;
            return true;
        }

    public:
        // 枚举指定路径下的所有文件名
        static bool EnumFilePath(std::vector<std::string> *files_list, const std::string &src_path = SRC_PATH)
        {
            LOG(DEBUG, "开始加载[%s]路径下的所有html文件!\n", src_path.c_str());
            namespace fs = boost::filesystem;
            // 定义目录path对象
            fs::path root(src_path);
            if (!fs::exists(root))
            {
                LOG(FATAL, "用户指定的%s路径是无效的!\n", src_path.c_str());
                return false;
            }
            // 递归遍历root下的所有子文件
            fs::recursive_directory_iterator end;
            for (fs::recursive_directory_iterator iter(root); iter != end; iter++)
            {
                if (fs::is_regular(root))
                {
                    continue;
                }
                // 筛选后缀
                if (iter->path().extension() != HTML_SUGFFIX)
                {
                    continue;
                }
                files_list->push_back(iter->path().string());
            }
            LOG(DEBUG, "加载成功!\n");
            return true;
        }
        // 提取并解析files_list中存储的所有有效的.html文件
        static void ExtractAndParseFromHtml(const std::vector<std::string> &files_list, std::vector<DocInfo_t> *results)
        {
            // 1.先遍历files_list
            LOG(DEBUG, "开始解析docinfo!...\n");
            for (auto &filename : files_list)
            {
                std::string data;
                if (!FileTool::ReadFileToString(filename, &data))
                {
                    LOG(ERROR, "读取文件%s时异常!\n", filename.c_str());
                    continue;
                }
                DocInfo_t doc;
                // 解析标题
                if (!ParseTitle(data, &doc._title))
                {
                    LOG(ERROR, "解析文件%s的title时异常!\n", filename.c_str());
                    continue;
                }
                // std::cout<<"文件: "<<filename<<",title: "<<doc._title<<std::endl;
                // 解析内容
                if (!ParseContent(data, &doc._content))
                {
                    LOG(ERROR, "解析文件%s的content时异常!\n", filename.c_str());
                    continue;
                }
                // std::cout<<"content: "<<doc._content<<std::endl;
                // 解析url
                if (!ParseUrl(filename, &doc._url))
                {
                    LOG(ERROR, "解析文件%s的URL时异常!\n", filename.c_str());
                    continue;
                }
                // std::cout<<"url: "<<doc._url<<std::endl;
                results->push_back(std::move(doc));
            }
            LOG(DEBUG, "解析done!\n");
        }
        // 将解析后的结果写入raw.txt文件
        static bool SaveHtml(const std::vector<DocInfo_t> &results, const std::string &raw_path = RAW_PATH)
        {
            // 格式：title\3content\3url\3\ntitle\3content\3url\3.....
            std::ofstream out(raw_path, std::ios::out | std::ios::binary);
            if (!out.is_open())
            {
                LOG(ERROR, "打开文件%s失败!\n", raw_path.c_str());
                return false;
            }
            for (auto &doc : results)
            {
                std::string one_info = doc._title + SEP;
                one_info += doc._content;
                one_info += SEP;
                one_info += doc._url;
                one_info += LINE_BREAKS;
                // std::cout<<"one : "<<one_info<<std::endl;
                out.write(one_info.c_str(), one_info.size());
            }
            out.close();
            return true;
        }
    };
    const  char* const DICT_PATH  = "/home/ubuntu/Lhd/Project/Boost_Search_Engines/cppjieba/dict/jieba.dict.utf8";
    const  char* const HMM_PATH= "/home/ubuntu/Lhd/Project/Boost_Search_Engines/cppjieba/dict/hmm_model.utf8";
    const  char* const USER_DICT_PATH= "/home/ubuntu/Lhd/Project/Boost_Search_Engines/cppjieba/dict/user.dict.utf8";
    const  char* const IDF_PATH  = "/home/ubuntu/Lhd/Project/Boost_Search_Engines/cppjieba/dict/idf.utf8";
    const  char* const STOP_WORD_PATH= "/home/ubuntu/Lhd/Project/Boost_Search_Engines/cppjieba/dict/stop_words.utf8";
    // 分词工具类
    class JiebalUtil
    {
    private:
        static cppjieba::Jieba jieba;
    public:
        static void CutFromString(const std::string& src,std::vector<std::string>* out)
        {
            jieba.CutForSearch(src,*out);
        }
    };
    //静态成员类外初始化
    cppjieba::Jieba JiebalUtil::jieba(DICT_PATH,HMM_PATH,USER_DICT_PATH,IDF_PATH,STOP_WORD_PATH);
}
